home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / fragment tool / dragstuff.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  13.3 KB  |  521 lines

  1. /*
  2.     File:        DragStuff.c
  3.  
  4.     Contains:    Drag Manager handlers and associated routines
  5.  
  6.     Written by: Chris White    
  7.  
  8.     Copyright:    Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 8/5/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.  
  24. // System includes
  25. #ifndef __DRAG__
  26.     #include <Drag.h>
  27. #endif
  28.  
  29. #ifndef __ERRORS__
  30.     #include <Errors.h>
  31. #endif
  32.  
  33. #ifndef __GESTALT__
  34.     #include <Gestalt.h>
  35. #endif
  36.  
  37. #ifndef __CODEFRAGMENTS__
  38.     #include <CodeFragments.h>
  39. #endif
  40.  
  41. #ifndef __SCRIPT__
  42.     #include <Script.h>
  43. #endif
  44.  
  45. #ifndef __RESOURCES__
  46.     #include <Resources.h>
  47. #endif
  48.  
  49. #ifndef __STDDEF__
  50.     #include <stddef.h>
  51. #endif
  52.  
  53.  
  54.  
  55. // Application includes
  56. #ifndef __FRAGMENTTOOL__
  57.     #include "FragmentTool.h"
  58. #endif
  59.  
  60. #ifndef __PROTOTYPES__
  61.     #include "Prototypes.h"
  62. #endif
  63.  
  64.  
  65. #include "Utilities.h"
  66.  
  67.  
  68.  
  69. static Boolean gHasAcceptableDrag = false;
  70. static Boolean gHasHilitedList = false;
  71.  
  72.  
  73.  
  74.  
  75. static Boolean DragItemsAreAcceptable ( DragReference theDrag );
  76. static Boolean DragIsNotInSourceWindow ( DragReference theDrag );
  77.  
  78.  
  79. static pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow,
  80.                                     void *handlerRefCon, DragReference theDrag );
  81. static pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow,
  82.                                     void *handlerRefCon, DragReference theDrag );
  83. static pascal OSErr DragReceiver ( WindowRef theWindow, void *handlerRefCon, 
  84.                                     DragReference theDrag );
  85. static pascal OSErr SendDataProc ( FlavorType theType,  void *dragSendRefCon,
  86.                                      ItemReference theItemRef, DragReference theDragRef );
  87.  
  88.  
  89.  
  90. //
  91. // InitDragHandlers creates the UPPs for the Drag Manager 
  92. // callback routines _if_ the Drag Manager is available.
  93. //
  94. OSErr InitDragHandlers ( void )
  95. {
  96.     OSErr    theErr = noErr;
  97.     
  98.     if ( gHasDragManager )
  99.     {
  100.         gDragTrackingHandlerUPP = NewDragTrackingHandlerProc ( DragTracker );
  101.         gDragReceiveHandlerUPP = NewDragReceiveHandlerProc ( DragReceiver );
  102.         gDragSendDataProcUPP = NewDragSendDataProc ( SendDataProc );
  103.     }
  104.     
  105.     return theErr;
  106. }
  107.  
  108.  
  109.  
  110. //
  111. // InstallDragHandlers attaches the tracking and receive handlers to
  112. // one of the application's windows.
  113. //
  114. OSErr InstallDragHandlers ( WindowRef theWindow )
  115. {    
  116.     OSErr     theErr = noErr;
  117.     
  118.     if ( gHasDragManager )
  119.     {
  120.         
  121.         theErr = InstallTrackingHandler ( gDragTrackingHandlerUPP, theWindow, nil );
  122.     
  123.         if ( theErr == noErr )
  124.         {
  125.             theErr = InstallReceiveHandler ( gDragReceiveHandlerUPP, theWindow, nil );
  126.             if ( theErr )
  127.                 (void) RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  128.         }
  129.         
  130.     }
  131.     
  132.     return theErr;
  133. }
  134.  
  135.  
  136.  
  137. //
  138. // RemoveDragHandlers removes the tracking and receive handlers from
  139. // one of the application's windows (usually just prior to disposal).
  140. //
  141. void RemoveDragHandlers ( WindowRef theWindow )
  142. {
  143.     if ( gHasDragManager )
  144.     {
  145.         
  146.         RemoveReceiveHandler ( gDragReceiveHandlerUPP, theWindow );
  147.         RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  148.         
  149.     }
  150.     
  151.     return;
  152. }
  153.  
  154.  
  155.  
  156. //
  157. // DragItemsAreAcceptable returns true if the contents (data) of
  158. // the drag are acceptable. This is called by the tracking and 
  159. // receive handlers.
  160. //
  161. static Boolean DragItemsAreAcceptable ( DragReference theDrag )
  162. {
  163.     OSErr            theErr;
  164.     unsigned short    totalItems;
  165.     ItemReference    itemRef;
  166.     Boolean            bAcceptIt;
  167.     OSType            currOSType;
  168.     Size            flavorDataSize;
  169.     
  170.     
  171.     bAcceptIt = false;
  172.     
  173.     
  174.     // this app can only accept the drag of a single item
  175.     theErr = CountDragItems ( theDrag, &totalItems );
  176.     
  177.     if ( theErr == noErr && totalItems == 1 )
  178.     {
  179.         // get the reference number of the dragged item
  180.         theErr = GetDragItemReferenceNumber ( theDrag, 1, &itemRef );
  181.  
  182.         if ( theErr == noErr )
  183.         {
  184.             // check if the item is one of ours
  185.             flavorDataSize = sizeof ( OSType );
  186.             theErr = GetFlavorData ( theDrag, itemRef, kCreatorCode, &currOSType,
  187.                                         &flavorDataSize, 0 );
  188.             
  189.             //#if DEBUGGING
  190.             //if ( theErr ) DebugStr ( "\p GetFlavorData returned an error" );
  191.             //#endif
  192.             
  193.             if ( theErr == noErr ) 
  194.                 bAcceptIt = true;
  195.         }
  196.     }
  197.     return bAcceptIt;
  198. }
  199.  
  200.  
  201.  
  202. //
  203. // DragIsNotInSourceWindow returns true if the drag in progress
  204. // is not in the same window it originated in. This is called by
  205. // the tracking and receive handlers.
  206. //
  207. static Boolean DragIsNotInSourceWindow ( DragReference theDrag )
  208. {
  209.     DragAttributes currDragFlags;
  210.     
  211.     GetDragAttributes ( theDrag, &currDragFlags );
  212.     return !(currDragFlags & kDragInsideSenderWindow);
  213. }
  214.  
  215.  
  216.  
  217. //
  218. // DragTracker is called by the drag manager whenever a drag is
  219. // over one of the application's windows. Upon entry, the Drag
  220. // Manager has already set the current port current to ‘theWindow’.
  221. pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow,
  222.                             void* handlerRefCon, DragReference theDrag )
  223. {
  224.     #pragma unused(handlerRefCon)
  225.     RgnHandle    tempRgn;
  226.     Boolean        mouseInList;
  227.     OSErr        err;
  228.     
  229.     err = noErr;
  230.  
  231.     switch ( theMessage )
  232.     {
  233.         case kDragTrackingEnterHandler:
  234.             
  235.             // Any initialization for this window handler.
  236.             gHasAcceptableDrag = DragItemsAreAcceptable ( theDrag );
  237.             gHasHilitedList = false;
  238.             
  239.             // Let the drag manager know if we can't accept this drag
  240.             if ( !gHasAcceptableDrag )
  241.                 err = dragNotAcceptedErr;
  242.             break;
  243.             
  244.         case kDragTrackingEnterWindow: 
  245.         case kDragTrackingInWindow:
  246.         case kDragTrackingLeaveWindow:
  247.             
  248.             // Highlighting of the window during a drag is done
  249.             // here.  Do it only if we can accept these items
  250.             // and we're not in the source window...
  251.             
  252.             if ( gHasAcceptableDrag )
  253.             {
  254.                 // Unless the mouse is leaving the visible area of the
  255.                 // window, check if it's in the window's content region
  256.                 
  257.                 mouseInList = false;
  258.                 if ( theMessage != kDragTrackingLeaveWindow )
  259.                 {
  260.                     if ( DragIsNotInSourceWindow ( theDrag ) )
  261.                     {
  262.                         Point localPt;
  263.                     
  264.                         (void) GetDragMouse ( theDrag, &localPt, 0L );
  265.                         GlobalToLocal ( &localPt );
  266.                         mouseInList = PtInList ( localPt, GetWListRef ( theWindow ) );
  267.                     }
  268.                 }
  269.                 
  270.                 // If the mouse is in the list and it isn't hilited...
  271.                 if ( mouseInList && !gHasHilitedList )
  272.                 {
  273.                     Rect    nuSpaceRect;
  274.  
  275.                     GetListRect ( &nuSpaceRect, GetWListRef ( theWindow ) );
  276.  
  277.                     tempRgn = NewRgn ( );
  278.                     RectRgn ( tempRgn, &nuSpaceRect );
  279.  
  280.                     // ...draw the hilight...
  281.                     if ( ShowDragHilite ( theDrag, tempRgn, true ) == noErr )
  282.                         // ... and remember it's now hilited
  283.                         gHasHilitedList = mouseInList;
  284.                     
  285.                     DisposeRgn ( tempRgn );
  286.                 }
  287.                 
  288.                 // else if the mouse is not in the list and the window is hilited...
  289.                 else if ( !mouseInList && gHasHilitedList )
  290.                     // ...erase the hilight...
  291.                     if ( HideDragHilite ( theDrag ) == noErr )
  292.                         // ...remember that nothing is hilited
  293.                         gHasHilitedList = false;
  294.             }
  295.             break;
  296.  
  297.         // do nothing for the leaveHandler message
  298.         case kDragTrackingLeaveHandler:
  299.             break;
  300.         
  301.         // let the drag manager know if we didn't recognize the message
  302.         default:
  303.             err = paramErr;
  304.     }
  305.     
  306.     return err;
  307. }
  308.  
  309.  
  310.  
  311. //
  312. // DragReceiver is called by the drag manager whenever an
  313. // item is dropped on one of the application's windows.
  314. //
  315. pascal OSErr DragReceiver ( WindowRef theWindow, void *handlerRefCon, DragReference theDrag )
  316. {
  317. #pragma unused (theWindow, handlerRefCon)
  318.  
  319.     Boolean            bMove;
  320.     ItemReference    itemRef;
  321.     Size            dataSize;
  322.     OSErr            err = noErr;
  323.     unsigned short    numItems, counter;
  324.     tDragData        theData;
  325.     int16            mouseDownModifiers, mouseUpModifiers;
  326.     
  327.     
  328.     
  329.     if (!DragItemsAreAcceptable(theDrag) || (gHasHilitedList == false))
  330.         return dragNotAcceptedErr;
  331.     
  332.                         
  333.     err = GetDragModifiers ( theDrag, nil, &mouseDownModifiers, &mouseUpModifiers );
  334.     if ( err )    goto CleanupAndBail;
  335.     bMove = !((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey));
  336.  
  337.     CountDragItems(theDrag, &numItems);
  338.     for (counter = 1; counter <= numItems; counter++)
  339.     {
  340.         err = GetDragItemReferenceNumber(theDrag, counter, &itemRef);
  341.         if (err != noErr)  goto CleanupAndBail;
  342.         
  343.         dataSize = sizeof ( tDragData );
  344.         err = GetFlavorDataSize ( theDrag, itemRef, kCreatorCode, &dataSize );
  345.         if ( dataSize == sizeof ( tDragData ) )
  346.             err = GetFlavorData ( theDrag, itemRef, kCreatorCode, &theData, &dataSize, 0 );
  347.         
  348.         
  349.         // We're going to first remove the drag hilite from here, because
  350.         // not doing so would result in a cleared out list rect, and when
  351.         // we go through the trackingLeaveWindow message up above, the
  352.         // HideDragHilite() call would draw the border again (since it's drawn
  353.         // in XOr mode).
  354.         
  355.         if ( gHasHilitedList )
  356.         {
  357.             if ( HideDragHilite ( theDrag ) == noErr)
  358.                 // remember that nothing is hilited
  359.                 gHasHilitedList = false;
  360.         }
  361.         
  362.         
  363.         if ( bMove )
  364.             err = MoveWindowFragment ( theData.theWindow, theData.theIndex, theWindow );
  365.         else
  366.             err = CopyWindowFragment ( theData.theWindow, theData.theIndex, theWindow );
  367.     }
  368.     
  369. CleanupAndBail:
  370.     return err;
  371. }
  372.  
  373.  
  374.  
  375. //
  376. // This is the Drag Manager's SendDataProc. It's called after a successful drag, when
  377. // the target application wants some data that was promised by the source application.
  378. //
  379. pascal OSErr SendDataProc ( FlavorType theType,  void* dragSendRefCon,
  380.                           ItemReference theItemRef, DragReference theDragRef )
  381.  
  382. {
  383.     // Gotcha: If we had just dragged out to the Finder, and we had a windowKind of
  384.     // 20, the system would have crashed by now. Why? The Finder uses a windowKind
  385.     // of 20, and thinks this is a drag from one of its windows. It then starts
  386.     // interpreting the window's refCon and inevitably doesn't like what it finds.
  387.     // A windowKind of 20 is now reserved for use by the system.
  388.     
  389.     
  390.     OSErr        result = noErr;
  391.     FSSpec        locationSpec;
  392.     tHeaderHan    theHeader = nil;
  393.     hdrHand        theResource = nil;
  394.  
  395.     
  396.     
  397.     // We use the file type for the HFSPromise flavor type
  398.     // and we don't handle any other pomised flavour types.
  399.     if ( theType != kCFragLibraryFileType )
  400.         cantGetFlavorErr;
  401.     
  402.     
  403.     result = CreateTemporaryFile ( &locationSpec );
  404.     if ( result == noErr )
  405.     {
  406.         OSErr            theErr;
  407.         int16            theIndex = 0;
  408.         int16            theRef, saveFile;
  409.         WindowRef        theWindow;
  410.         ListRef            theList;
  411.         tWindowInfoPtr    theInfo;
  412.         
  413.         theWindow = (WindowRef) dragSendRefCon;
  414.         theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow );
  415.         theList = GetWListRef ( theWindow );
  416.         
  417.         theHeader = (tHeaderHan) NewHandleClear ( sizeof ( tHeader ) );
  418.         theErr = MemError ( );
  419.         if ( theErr ) goto CleanupAndBail;
  420.         
  421.         (*theHeader)->version = 1;        // Current version number
  422.         
  423.         // First, we'll add the content to the file
  424.         while ( GetSelection ( theList, &theIndex ) )
  425.         {
  426.             int16    itemIndex;
  427.             
  428.             itemIndex = GetIndexFromNthWindowItem ( theWindow, theIndex );
  429.             theErr = CopyFragment ( (tHeaderHan) theInfo->dataHandle, &theInfo->fileSpec,
  430.                                         itemIndex, theHeader, &locationSpec );
  431.             if ( theErr )    goto CleanupAndBail;
  432.             theIndex++;
  433.         }
  434.         
  435.         theResource = (hdrHand) NewHandleClear ( offsetof ( cfrgHeader, arrayStart ) );
  436.         (*theResource)->version = 1;        // Current version number
  437.         
  438.         theErr = BuildResource ( (tHeaderHan) theHeader, (Handle) theResource );
  439.         if ( theErr )    goto CleanupAndBail;
  440.         
  441.         saveFile = CurResFile ( );
  442.         theRef = FSpOpenResFile ( &locationSpec, fsRdWrPerm );
  443.         // If the file is already open, it may not be the current resource file
  444.         UseResFile ( theRef );
  445.         AddResource ( (Handle) theResource, kCFragResourceType, kCFragResourceID, "\p" );
  446.         UpdateResFile ( theRef );
  447.         ReleaseResource ( (Handle) theResource );
  448.         CloseResFile ( theRef );
  449.         UseResFile ( saveFile );
  450.         
  451.         
  452.         // Now, set the flavor data of our kCFragLibraryFileType flavor 
  453.         // with an FSSpec to the new file.
  454.         result = SetDragItemFlavorData ( theDragRef, theItemRef, theType,
  455.                                             &locationSpec, sizeof ( FSSpec ), 0L );
  456.         
  457.     }
  458.     
  459.     
  460.     // Any errors? Return a cantGetFlavorErr
  461.     if ( result )
  462.         result = cantGetFlavorErr;
  463.         
  464.     return result;
  465.     
  466. CleanupAndBail:
  467.     
  468.     // We'll leave the temp file for the system to handle. It
  469.     // could still be useful, if only for debugging purposes.
  470.      if ( theHeader )
  471.          DisposeHandle ( (Handle) theHeader );
  472.      
  473.      
  474.      if ( theResource )
  475.      {
  476.         if ( IsAResource ( (Handle) theResource ) )
  477.             ReleaseResource ( (Handle) theResource );
  478.         else
  479.             DisposeHandle ( (Handle) theResource );
  480.     }
  481.     
  482.     return cantGetFlavorErr;
  483. }
  484.  
  485.  
  486.  
  487. //
  488. // This routine just promises the Drag Manager that we'll create a file
  489. // if the user drags out to the Finder
  490. //
  491. OSErr AddHFSPromise ( DragReference theDrag, ItemReference theItem )
  492. {
  493.     OSErr                theErr;
  494.     PromiseHFSFlavor    thePromise;
  495.     
  496.     
  497.     // Here's what we're going to promise to create in our send proc
  498.     thePromise.fileType = kCFragLibraryFileType;
  499.     thePromise.fileCreator = kFourQuestionMarks;
  500.     thePromise.fdFlags = 0;
  501.     thePromise.promisedFlavor = kCFragLibraryFileType;
  502.     
  503.     
  504.     // The promised flavor can be anything, just as long as we add a drag item
  505.     // that has the same type, and set it in our send proc. Failure to do this
  506.     // will cause the zoomback.
  507.     
  508.     theErr = AddDragItemFlavor ( theDrag, theItem, flavorTypePromiseHFS, &thePromise,
  509.                                         sizeof ( PromiseHFSFlavor ), 0L );
  510.     if ( theErr == noErr )
  511.         // Here's the promised flavor we're going to deliver
  512.         theErr = AddDragItemFlavor ( theDrag, theItem, kCFragLibraryFileType, nil, 0L, 0L );
  513.  
  514.     
  515.     return theErr;
  516. }
  517.  
  518.  
  519.  
  520.  
  521.